/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "inifile.h"
#include <fstream>
#include <iostream>
#include "node/sys/console.h"
#include "node/util/string.h"
using Console = ::node::sys::Console;

#define MAX_LINE_SIZE   0x200

namespace node {
    namespace util {

        IniFile::IniFile(void)
            : ini_()
            , lastest_section_(-1)
        {

        }

        IniFile::~IniFile(void)
        {

        }

        bool IniFile::Open(const char *filename)
        {
            do 
            {
                ::std::ifstream ifs(filename);
                if (!ifs.is_open()) {
                    char buf[0x200] = { 0 };
                    Console::Error("[IniFile::Open] open %s failed, %s\n", filename, 
                        ::node::util::strerror(buf, sizeof(buf), errno));
                    break;
                }

                char line[MAX_LINE_SIZE];
                while (!ifs.eof())
                {
                    ifs.getline(line, MAX_LINE_SIZE);
                    if (!ParseLine(::std::string(line))) {
                        return false;
                    }
                }
                return true;
            } while (false);
            return false;
        }

        const char *IniFile::GetString(const char *section, const char *key, const char *default /* = NULL */)
        {
            ::std::vector<struct Section>::const_iterator iter = ini_.begin();
            for (; iter != ini_.end(); ++iter) {
                if (iter->name != section) {
                    continue;
                }

                ::std::vector<struct KVPair>::const_iterator iter_kv = iter->data.begin();
                for (; iter_kv != iter->data.end(); ++iter_kv) {
                    if (iter_kv->key == key) {
                        return iter_kv->value.c_str();
                    }
                }
            }
            return default;
        }

        int IniFile::GetInt(const char *section, const char *key, int default /* = 0 */)
        {
            ::std::vector<struct Section>::const_iterator iter = ini_.begin();
            for (; iter != ini_.end(); ++iter) {
                if (iter->name != section) {
                    continue;
                }

                ::std::vector<struct KVPair>::const_iterator iter_kv = iter->data.begin();
                for (; iter_kv != iter->data.end(); ++iter_kv) {
                    if (iter_kv->key == key) {
                        return atoi(iter_kv->value.c_str());
                    }
                }
            }
            return default;
        }

        long IniFile::GetLong(const char *section, const char *key, long default /* = 0L */)
        {
            ::std::vector<struct Section>::const_iterator iter = ini_.begin();
            for (; iter != ini_.end(); ++iter) {
                if (iter->name != section) {
                    continue;
                }

                ::std::vector<struct KVPair>::const_iterator iter_kv = iter->data.begin();
                for (; iter_kv != iter->data.end(); ++iter_kv) {
                    if (iter_kv->key == key) {
                        return atol(iter_kv->value.c_str());
                    }
                }
            }
            return default;
        }

        double IniFile::GetDouble(const char *section, const char *key, double default /* = 0.0 */)
        {
            ::std::vector<struct Section>::const_iterator iter = ini_.begin();
            for (; iter != ini_.end(); ++iter) {
                if (iter->name != section) {
                    continue;
                }

                ::std::vector<struct KVPair>::const_iterator iter_kv = iter->data.begin();
                for (; iter_kv != iter->data.end(); ++iter_kv) {
                    if (iter_kv->key == key) {
                        return atof(iter_kv->value.c_str());
                    }
                }
            }
            return default;
        }

        bool IniFile::ParseLine(::std::string &line)
        {
            // trim space
            line.erase(0, line.find_first_not_of(" "));
            if (line.find_last_of(";") != ::std::string::npos) {
                line.erase(line.find_last_of(";") - 1);
            }
            line.erase(line.find_last_not_of(" ") + 1);

            if (line.empty() || line[0] == ';') {
                return true;
            }

            int pos = -1;
            if (line[0] == '[') {
                // parse section
                pos = line.rfind(']');
                if (pos == ::std::string::npos) {
                    Console::Error("[InitFile::ParseLine] parse failed, %s\n", line.c_str());
                    return false;
                }

                struct Section section;
                section.name.append(line, 1, pos - 1);
                ::node::util::TrimString(section.name, " ");
                ini_.push_back(section);
                ++lastest_section_;
                return true;
            }

            pos = line.find('=');
            if (pos != ::std::string::npos) {
                // parse kv
                struct KVPair kvpair;
                kvpair.key.append(line, 0, pos);
                kvpair.value.append(line, pos + 1, line.length());
                ::node::util::TrimString(kvpair.key, " ");
                ::node::util::TrimString(kvpair.value, " ");

                if ((lastest_section_ < 0) || (lastest_section_ < (int)(ini_.size() - 1))) {
                    struct Section section;
                    section.name = "__default";
                    section.data.push_back(kvpair);
                    ini_.push_back(section);
                    ++lastest_section_;
                }
                else {
                    ini_[lastest_section_].data.push_back(kvpair);
                }
            }
            return true;
        }
    }
}